﻿using System;
using biz.ritter.javapi.lang.reflect;
using biz.ritter.javapi.util;
using Nofs.Net.nofs.metadata.interfaces;
using Nofs.Net.Common.Interfaces.Domain;
using Nofs.Net.Common.Interfaces.Library;
using Nofs.Net.Common.Interfaces.Cache;
using Nofs.Fuse;
using Nofs.Net.Domain.Impl;

namespace Nofs.Net.Fuse.Impl
{

    public class FileObjectFactory : IFileObjectFactory
    {
        private IAttributeAccessor _accessor;
        private IStatMapper _statMapper;
        private IKeyCache _keyCache;

        public FileObjectFactory(IAttributeAccessor accessor, IStatMapper statMapper, IKeyCache keyCache)
        {
            _accessor = accessor;
            _statMapper = statMapper;
            _keyCache = keyCache;
        }

        //@Override
        public IFileObjectStat BuildStat(Object obj) //throws Exception 
        {
            if (obj is Method)
            {
                throw new System.Exception("can't build from method");
            }
            if (!_accessor.isDomainObject(obj) && !_accessor.isFolderObject(obj))
            {
                throw new System.Exception(obj.getClass().getName() + " is not a domain object");
            }
            int typ;
            if (_accessor.isFolderObject(obj))
            {
                typ = FuseFtypeConstants.TYPE_DIR;
            }
            else
            {
                typ = FuseFtypeConstants.TYPE_FILE;
            }
            FileObjectStat stat = new FileObjectStat(_keyCache.GetByReference(obj).getID(), _accessor.getNameFromObject(obj));
            stat.setMode(typ | 0755);
            return stat;
        }

        //@Override
        public IFileObjectStat BuildStat(Object obj, Method method) //throws Exception 
        {
            if (!_accessor.isDomainObject(method) && !_accessor.isFolderObject(method) &&
               !_accessor.isExecutable(method))
            {
                throw new Exception("not a domain object");
            }
            int typ;
            if (_accessor.isExecutable(method))
            {
                typ = FuseFtypeConstants.TYPE_FILE;
            }
            else
            {
                typ = FuseFtypeConstants.TYPE_DIR;
            }
            FileObjectStat stat = new FileObjectStat(_keyCache.GetByReference(obj).getID(), TranslateMethodName(method.getName()));
            stat.setMode(typ | 0755);
            return stat;
        }

        //@Override
        public IFileObject BuildFileObject(String parentPath, Object obj) //throws Exception
        {
            if (obj is Method)
            {
                throw new Exception("can't build from method");
            }
            if (!_accessor.isDomainObject(obj) && !_accessor.isFolderObject(obj))
            {
                throw new Exception("not a domain object");
            }
            GenerationType generation;
            if (_accessor.isFolderObject(obj))
            {
                generation = GenerationType.DOMAIN_FOLDER;
            }
            else
            {
                generation = GenerationType.DATA_FILE;
            }
            FileObject file = new FileObject(parentPath, obj, _accessor, generation);
            file.SetID(_keyCache.GetByReference(obj).getID());
            file.SetStatLazyLoader(new StatLazyLoader(_statMapper, file));
            return file;
        }

        //@Override
        public IFileObject BuildFileObject(String parentPath, Object obj, Method method)// throws Exception 
        {
            if (!_accessor.isDomainObject(method) && !_accessor.isFolderObject(method) &&
               !_accessor.isExecutable(method))
            {
                throw new Exception("not a domain object");
            }
            GenerationType generation;
            if (_accessor.isExecutable(method))
            {
                generation = GenerationType.EXECUTABLE;
            }
            else
            {
                generation = GenerationType.DOMAIN_FOLDER;
            }
            FileObject file = new FileObject(parentPath, obj, method, _accessor, generation);
            file.SetID(_keyCache.GetByReference(obj).getID());
            file.SetStatLazyLoader(new StatLazyLoader(_statMapper, file));
            return file;
        }

        private static String TranslateMethodName(String name)
        {
            if (name.startsWith("get") && name.length() > 3)
            {
                name = name.substring(3);
            }
            return name;
        }

        //@Override
        //@SuppressWarnings("unchecked")
        public Collection<String> GetChildNames(Object methodObj, Method parentMethod) //throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, Exception 
        {
            if (_accessor.isFolderObject(parentMethod))
            {
                return GetChildNames(parentMethod.invoke(methodObj, (Object[])null));
            }
            else if (parentMethod.getReturnType().isAssignableFrom(typeof(List).getClass()))
            {
                Collection<String> names = new LinkedList<String>();
                LinkedList<Object> objects = (LinkedList<Object>)parentMethod.invoke(methodObj, (Object[])null);
                foreach (Object obj in objects)
                {
                    names.add(_accessor.getNameFromObject(obj));
                }
                return Collections.unmodifiableCollection(names);
            }
            return new LinkedList<String>();
        }

        //@Override
        //@SuppressWarnings("unchecked")
        public Collection<String> GetChildNames(Object parent) //throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, Exception 
        {
            LinkedList<String> names = new LinkedList<String>();
            if (parent is List)
            {
                foreach (Object childObj in (LinkedList<Object>)parent)
                {
                    if (_accessor.isDomainObject(childObj) ||
                       _accessor.isFolderObject(childObj))
                    {
                        names.add(_accessor.getNameFromObject(childObj));
                    }
                }
            }
            else
            {
                foreach (Method method in parent.getClass().getDeclaredMethods())
                {
                    String methodName = TranslateMethodName(method.getName());
                    if (_accessor.isDomainObject(method) ||
                       _accessor.isFolderObject(method) ||
                       _accessor.isExecutable(method))
                    {
                        names.add(methodName);
                    }
                }
            }
            return Collections.unmodifiableCollection(names);
        }

        //@Override
        //@SuppressWarnings("unchecked")
        public Object GetChildWithName(Object methodObj, Method parentMethod, String name) //throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, Exception 
        {
            Object child = null;
            if (parentMethod.getReturnType().isAssignableFrom(typeof(List).getClass()))
            {
                LinkedList<Object> objects = (LinkedList<Object>)parentMethod.invoke(methodObj, (Object[])null);
                foreach (Object obj in objects)
                {
                    if (_accessor.getNameFromObject(obj).compareTo(name) == 0)
                    {
                        child = obj;
                        break;
                    }
                }
            }
            return child;
        }

        //@Override
        //@SuppressWarnings("unchecked")
        public Object GetChildWithName(Object parent, String name) //throws Exception 
        {
            Object child = null;
            foreach (Method method in parent.getClass().getDeclaredMethods())
            {
                if (TranslateMethodName(method.getName()).compareTo(name) == 0)
                {
                    if (method.getReturnType() == typeof(void).getClass()
                        || _accessor.isDomainObject(method))
                    {
                        child = method;
                    }
                    else
                    {
                        child = method.invoke(parent, (Object[])null);
                    }
                    break;
                }
            }
            if (child == null && typeof(List).getClass().isAssignableFrom(parent.getClass()))
            {
                foreach (Object obj in (LinkedList<Object>)parent)
                {
                    if (_accessor.getNameFromObject(obj).compareTo(name) == 0)
                    {
                        child = obj;
                        break;
                    }
                }
            }
            return child;
        }
    }

}
